package pdfsetter.core.schema.advanced;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import pdfsetter.core.schema.FillingSchema;
import pdfsetter.core.schema.PageSelection;
import pdfsetter.core.util.ArraysExtension;
import pdfsetter.core.util.RandomAccessIterable;
import pdfsetter.core.util.RandomAccessIterableIterator;

/**
 * ConsecutiveFileListProcessPattern is such a {@link FillingSchemaPattern} in which we have some fixed list of files and assigned selectors. We take pages from
 * these files processing them entirely one by one according to the list order. We choose pages with selectors which are attached to files.
 * 
 * @author Andrzej Dorobisz
 */
public class ConsecutiveFileListProcessPattern implements FillingSchemaPattern {
    private final AttachedFilePageSelector[] attachedSelectors;
    private final int expectedFileNumber;

    public ConsecutiveFileListProcessPattern(List<AttachedFilePageSelector> filesAndSelectors) {
        attachedSelectors = new AttachedFilePageSelector[filesAndSelectors.size()];
        // TODO exceptions, what if file number is less than zero
        int maxFileNumber = -1;
        int i = 0;
        for (AttachedFilePageSelector attachedSelector : filesAndSelectors) {
            if (attachedSelector.getFileNumber() > maxFileNumber)
                maxFileNumber = attachedSelector.getFileNumber();
            attachedSelectors[i++] = attachedSelector;
        }
        expectedFileNumber = maxFileNumber + 1;
    }

    @Override
    public FillingSchema produceFillingSchema(int[] fileSizes) {
        if (expectedFileNumber > fileSizes.length)
            throw new RuntimeException(); // TODO error!
        List<AttachedFilePageSelection> attachedSelections = new LinkedList<AttachedFilePageSelection>();
        // AttachedFilePageSelection[] attachedSelections = new AttachedFilePageSelection[attachedSelectors.length];
        for (int i = 0; i < attachedSelectors.length; i++) {
            AttachedFilePageSelection curSelection = attachedSelectors[i].produceAttachedSelection(fileSizes[attachedSelectors[i].getFileNumber()]);
            if (curSelection.getFilePageSelection().size() > 0)
                attachedSelections.add(curSelection);
        }
        return new ConsecutiveFileListFillingSchema(attachedSelections);
    }

    @Override
    public int getExpectedNumberOfFiles() {
        return expectedFileNumber;
    }

    private class ConsecutiveFileListFillingSchema implements FillingSchema {
        private final AttachedFilePageSelection[] pageSelections;
        private final int size;
        private final int[] selectionsBegginings;

        public ConsecutiveFileListFillingSchema(List<AttachedFilePageSelection> attachedSelections) {
            int length = attachedSelections.size();
            pageSelections = attachedSelections.toArray(new AttachedFilePageSelection[0]);
            selectionsBegginings = new int[length];
            if (length <= 0) {
                size = 0;
            } else {
                selectionsBegginings[0] = 0;
                for (int i = 1; i < length; i++) {
                    selectionsBegginings[i] = selectionsBegginings[i - 1] + pageSelections[i - 1].getFilePageSelection().size();
                }
                size = selectionsBegginings[length - 1] + pageSelections[length - 1].getFilePageSelection().size();
            }
        }

        @Override
        public RandomAccessIterable<PageSelection> getPageSelections() {
            return new RandomAccessIterable<PageSelection>() {
                @Override
                public Iterator<PageSelection> iterator() {
                    return new RandomAccessIterableIterator<PageSelection>(this);
                }

                @Override
                public PageSelection get(int index) {
                    if (index < 0 || index >= size)
                        throw new IndexOutOfBoundsException();
                    int selectionNumber = ArraysExtension.floorSearch(selectionsBegginings, index);
                    return pageSelections[selectionNumber].getPageSelection(index - selectionsBegginings[selectionNumber]);
                }

                @Override
                public int size() {
                    return size;
                }
            };
        }

        @Override
        public int getExpectedNumberOfFiles() {
            return expectedFileNumber;
        }
    }
}
